package utils3D 
{
	import flash.display.BitmapData;
	import flash.geom.Bezier;
	import flash.geom.Point;
	import flash.geom.Vector3D;
	/**
	 * ...
	 * @author Dmitriy Gaiduk
	 */
	public class ShapeFactory 
	{
		
		public function ShapeFactory() 
		{
			
		}
		
		static public function getBoxData(width:Number, height:Number, depth:Number, texture:BitmapData):Array
		{
			//calculate half of width, height and depth
			var w2:Number = width*0.5;
			var h2:Number = height*0.5;
			var d2:Number = depth*0.5;
 
			//define box points
			var points:Vector.<Vector3D> = new Vector.<Vector3D>();
			points.push(new Vector3D(-w2, -h2, -d2));
			points.push(new Vector3D(w2, -h2, -d2));
			points.push(new Vector3D(w2, h2, -d2));
			points.push(new Vector3D(-w2, h2, -d2));
			points.push(new Vector3D(-w2, -h2, d2));
			points.push(new Vector3D(w2, -h2, d2));
			points.push(new Vector3D(w2, h2, d2));
			points.push(new Vector3D(-w2, h2, d2));
 
			//define indices
			var indicesFront:Vector.<int> = new Vector.<int>();
			indicesFront.push(0, 1, 3, 1, 2, 3);
			var indicesBack:Vector.<int> = new Vector.<int>();
			indicesBack.push(5, 4, 7, 6, 5, 7);
			var indicesLeft:Vector.<int> = new Vector.<int>();
			indicesLeft.push(1, 5, 2, 5, 6, 2);
			var indicesRight:Vector.<int> = new Vector.<int>();
			indicesRight.push(4, 0, 3, 4, 3, 7);
			var indicesTop:Vector.<int> = new Vector.<int>();
			indicesTop.push(4, 5, 0, 5, 1, 0);
			var indicesBottom:Vector.<int> = new Vector.<int>();
			indicesBottom.push(3, 2, 7, 2, 6, 7);
 
			//define uvt
			var uvtFront:Vector.<Number> = new Vector.<Number>();
			uvtFront.push(0, 0, NaN, 1, 0, NaN, 1, 1, NaN, 0, 1, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN);
			var uvtBack:Vector.<Number> = new Vector.<Number>();
			uvtBack.push(NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, 1, 0, NaN, 0, 0, NaN, 0, 1, NaN, 1, 1, NaN);
			var uvtLeft:Vector.<Number> = new Vector.<Number>();
			uvtLeft.push(NaN, NaN, NaN, 0, 0, NaN, 0, 1, NaN, NaN, NaN, NaN, NaN, NaN, NaN, 1, 0, NaN, 1, 1, NaN, NaN, NaN, NaN);
			var uvtRight:Vector.<Number> = new Vector.<Number>();
			uvtRight.push(1, 0, NaN, NaN, NaN, NaN, NaN, NaN, NaN, 1, 1, NaN, 0, 0, NaN, NaN, NaN, NaN, NaN, NaN, NaN, 0, 1, NaN);
			var uvtTop:Vector.<Number> = new Vector.<Number>();
			uvtTop.push(0, 1, NaN, 1, 1, NaN, NaN, NaN, NaN, NaN, NaN, NaN, 0, 0, NaN, 1, 0, NaN, NaN, NaN, NaN, NaN, NaN, NaN);
			var uvtBottom:Vector.<Number> = new Vector.<Number>();
			uvtBottom.push(NaN, NaN, NaN, NaN, NaN, NaN, 1, 0, NaN, 0, 0, NaN, NaN, NaN, NaN, NaN, NaN, NaN, 1, 1, NaN, 0, 1, NaN);
 
			return [points,
				[indicesFront, indicesBack, indicesLeft, indicesRight, indicesTop, indicesBottom],
				[uvtFront, uvtBack, uvtLeft, uvtRight, uvtTop, uvtBottom],
				[texture, texture, texture, texture, texture, texture]];
		}
		
		/**
		 * Make plane with 2 points
		 * @param	points
		 * @param	height
		 * @return
		 */
		static public function getPlaneDataXY(points:Vector.<Number>, height:Number):ShapeData
		{
			var p1:Point = new Point(points[0], points[1]);
			var p2:Point = new Point(points[2], points[3]);
			var vec:Point = new Point(p2.x - p1.x, p2.y - p1.y);
			var segmentCountH:int = 1;
			var segmentCountW:int = 1;
			var segmentH:Number = height / segmentCountH;
			var gridPoints:Vector.<Vector3D> = new Vector.<Vector3D>();
			var lineT:Number; //0..1
			var lineStep:Number = 1 / segmentCountW;
			var linePoint:Point = new Point();
			
			for (var h:int = 0; h <= segmentCountH; h++)
			{
				for (var ti:int = 0; ti <= segmentCountW; ti++)
				{
					lineT = lineStep * ti;
					linePoint.x = vec.x*lineT + p1.x;
					linePoint.y = vec.y*lineT + p1.y;
					if (ti == segmentCountW)
					{
						linePoint.x = p2.x;
						linePoint.y = p2.y;
					}
					gridPoints.push(new Vector3D(linePoint.x, h*segmentH, linePoint.y));
				}
			}
			
			//define indices
			var indices:Vector.<int> = new Vector.<int>();
			var pointsInRow:int = segmentCountW + 1;
			var pointsInColumn:int = segmentCountH + 1;
			for (var hi:int = 0; hi < segmentCountH; hi++ )
			{
				for (var wi:int = 0; wi < segmentCountW; wi++ )
				{
					indices.push(hi*pointsInRow+wi, hi*pointsInRow+wi+1,(hi+1)*pointsInRow+wi);
					indices.push(hi*pointsInRow+wi+1,(hi+1)*pointsInRow+wi+1,(hi+1)*pointsInRow+wi);
				}
			}
			
			//define uvt
			var uvt:Vector.<Number> = new Vector.<Number>();
			var uStep:Number = 1 / segmentCountW;
			var vStep:Number = 1 / segmentCountH;
			for (var vi:int = 0; vi < pointsInColumn; vi++ )
			{
				for (var ui:int = 0; ui < pointsInRow; ui++ )
				{
					uvt.push(ui*uStep, vi*vStep, NaN);
				}
			}
			
			
			return new ShapeData(gridPoints, indices, uvt);
			
		}
		
		static public function getBezierRect(points:Vector.<Number>, height:Number):ShapeData
		{
			var k: int = 0;
			var gridPoints:Vector.<Vector3D> = new Vector.<Vector3D>();
			
			// кол-во сегментов по оси Y для каждой кривой
			var segmentCountPerLineH:int = 1; // segments in Y axis for one curve
			// кол-во сегментов по оси X для каждой кривой
			var segmentCountPerLineW:int = 8; // segments in X axis for one curve
			
			// общее кол-во сегментов по оси Y. нарастание по оси X, высота по Y = height
			var segmentTotalCountH:int = segmentCountPerLineH; 
			// общее кол-во сегментов по оси X
			var segmentTotalCountW:int = 0; 
			// кол-во горизонтальных линий
			var totalLinesByX:int = 0;
			
			// нарастание по оси X, высота по Y = height
			totalLinesByX = int(points.length / 6);
			segmentTotalCountW = segmentCountPerLineW * totalLinesByX;
			var pointsInLine:int = 0;
			for (var h:int = 0; h <= segmentCountPerLineH; h++)
			{	
				k = 0;
				pointsInLine = 0;
				while (k < points.length)
				{
					// начальная точка, она же конечная точка предыдущей кривой для количества кривых >1
					var start:Point = new Point(points[k], points[k + 1]); 
					// точка управления, куда стремиться искривление
					var control:Point = new Point(points[k + 2], points[k + 3]); 
					// конечная
					var end:Point = new Point(points[k+4], points[k+5]); 
					
					var bezier:Bezier = new Bezier(start, control, end); // строим модель кривой, без представления
					// height of segment
					var segmentH:Number = height / segmentCountPerLineH;
					var bezierT:Number; //0..1
					var bezierStep:Number = 1 / segmentCountPerLineW;
					//define bezier points
					var bezierPoint:Point;
					var ti:int = k == 0 ? 0 :1;
					for (ti; ti <= segmentCountPerLineW; ti++)
					{
						bezierT = bezierStep * ti;
						if (ti == segmentCountPerLineW)
						{
							bezierT = 1;
						}
						bezierPoint = bezier.getPoint(bezierT);
						gridPoints.push(new Vector3D(bezierPoint.x, h * segmentH, bezierPoint.y));
						pointsInLine++;
					}

					k += 6; // 3 x pair point {x,y}
				}
			}
			
			//define indices
			var indices:Vector.<int> = new Vector.<int>();
			//var pointsInRow:int = (segmentCountPerLineW + 1) * totalLinesByX;
			var pointsInRow:int = pointsInLine;
			var pointsInColumn:int = segmentTotalCountH + 1;
			
			var index:int;
			trace("grid count: " + gridPoints.length);
			for (var i:int = 0; i < gridPoints.length; i++ ) {
				
				if (i < pointsInLine) {
					index = i;
				}else {
					index = gridPoints.length - 1 - i + pointsInLine;
				}
				trace("index: " + index);
				indices.push(index);
				
			}
			indices.push(0);
			
			//define uvt
			var uvt:Vector.<Number> = new Vector.<Number>();
			var uStep:Number = 1 / segmentTotalCountW;
			var vStep:Number = 1 / segmentTotalCountH;
			for (var vi:int = 0; vi < pointsInColumn; vi++ )
			{
				for (var ui:int = 0; ui < pointsInRow; ui++ )
				{
					uvt.push(ui*uStep, vi*vStep, NaN);
				}
			}
			// getBezierData: gridPoints: 66 indices: 120 uvt: 99
			// getBezierData: gridPoints: 33 indices: 120 uvt: 99
			return new ShapeData(gridPoints, indices, uvt);
		}
		
		
		
		
		static public function getBezierDataMulti(points:Vector.<Number>, height:Number):ShapeData
		{
			var k: int = 0;
			var gridPoints:Vector.<Vector3D> = new Vector.<Vector3D>();
			
			// кол-во сегментов по оси Y для каждой кривой
			var segmentCountPerLineH:int = 1; // segments in Y axis for one curve
			// кол-во сегментов по оси X для каждой кривой
			var segmentCountPerLineW:int = 8; // segments in X axis for one curve
			
			// общее кол-во сегментов по оси Y. нарастание по оси X, высота по Y = height
			var segmentTotalCountH:int = segmentCountPerLineH; 
			// общее кол-во сегментов по оси X
			var segmentTotalCountW:int = 0; 
			// кол-во горизонтальных линий
			var totalLinesByX:int = 0;
			
				// нарастание по оси X, высота по Y = height
			totalLinesByX = int(points.length / 6);
			segmentTotalCountW = segmentCountPerLineW * totalLinesByX;
			var pointsInLine:int = 0;
			for (var h:int = 0; h <= segmentCountPerLineH; h++)
			{	
				k = 0;
				pointsInLine = 0;
				while (k < points.length)
				{
					// начальная точка, она же конечная точка предыдущей кривой для количества кривых >1
					var start:Point = new Point(points[k], points[k + 1]); 
					// точка управления, куда стремиться искривление
					var control:Point = new Point(points[k + 2], points[k + 3]); 
					// конечная
					var end:Point = new Point(points[k+4], points[k+5]); 
					
					var bezier:Bezier = new Bezier(start, control, end); // строим модель кривой, без представления
					// height of segment
					var segmentH:Number = height / segmentCountPerLineH;
					var bezierT:Number; //0..1
					var bezierStep:Number = 1 / segmentCountPerLineW;
					//define bezier points
					var bezierPoint:Point;
					var ti:int = k == 0 ? 0 :1;
					for (ti; ti <= segmentCountPerLineW; ti++)
					{
						bezierT = bezierStep * ti;
						if (ti == segmentCountPerLineW)
						{
							bezierT = 1;
						}
						bezierPoint = bezier.getPoint(bezierT);
						gridPoints.push(new Vector3D(bezierPoint.x, h * segmentH, bezierPoint.y));
						pointsInLine++;
					}

					k += 6; // 3 x pair point {x,y}
				}
			}
			
			//define indices
			var indices:Vector.<int> = new Vector.<int>();
			//var pointsInRow:int = (segmentCountPerLineW + 1) * totalLinesByX;
			var pointsInRow:int = pointsInLine;
			var pointsInColumn:int = segmentTotalCountH + 1;
	
			for (var hi:int = 0; hi < segmentTotalCountH; hi++ )
			{
				for (var wi:int = 0; wi < segmentTotalCountW; wi++ )
				{
					indices.push(hi * pointsInRow + wi, hi * pointsInRow + wi + 1, (hi + 1) * pointsInRow + wi);
					indices.push(hi * pointsInRow + wi + 1, (hi + 1) * pointsInRow + wi + 1, (hi + 1) * pointsInRow + wi);
					//var p:Vector3D = gridPoints[(hi + 1) * pointsInRow + wi + 1];
					//trace(((hi + 1) * pointsInRow + wi + 1) + " p x: " + p.x + " z: " + p.z);
				}
			}
			
			var maxIndex:int = 0;
			for (var q:int = 0; q < indices.length; q++)
			{
				maxIndex = Math.max(maxIndex, indices[q]);
			}
			
			//define uvt
			var uvt:Vector.<Number> = new Vector.<Number>();
			var uStep:Number = 1 / segmentTotalCountW;
			var vStep:Number = 1 / segmentTotalCountH;
			for (var vi:int = 0; vi < pointsInColumn; vi++ )
			{
				for (var ui:int = 0; ui < pointsInRow; ui++ )
				{
					uvt.push(ui*uStep, vi*vStep, NaN);
				}
			}
			// getBezierData: gridPoints: 66 indices: 120 uvt: 99
			// getBezierData: gridPoints: 33 indices: 120 uvt: 99
			return new ShapeData(gridPoints, indices, uvt);
		}
		
		static public function getPlaneDataXZ(leftBottom:Point, topRight:Point, height:Number):ShapeData
		{
			var segmentCountH:int = 1;
			var segmentCountW:int = 1;
			var segmentH:Number = (topRight.y - leftBottom.y) / segmentCountH;
			var segmentW:Number = (topRight.x - leftBottom.x) / segmentCountW;
			var gridPoints:Vector.<Vector3D> = new Vector.<Vector3D>();
			for (var h:int = 0; h <= segmentCountH; h++)
			{
				for (var ti:int = segmentCountW; ti >= 0; ti--)
				{
					gridPoints.push(new Vector3D(topRight.x -  ti*segmentW, height, topRight.y - h*segmentH));
				}
			}
			
			//define indices
			var indices:Vector.<int> = new Vector.<int>();
			var pointsInRow:int = segmentCountW + 1;
			var pointsInColumn:int = segmentCountH + 1;
			for (var hi:int = 0; hi < segmentCountH; hi++ )
			{
				for (var wi:int = 0; wi < segmentCountW; wi++ )
				{
					indices.push(hi*pointsInRow+wi, hi*pointsInRow+wi+1,(hi+1)*pointsInRow+wi);
					indices.push(hi*pointsInRow+wi+1,(hi+1)*pointsInRow+wi+1,(hi+1)*pointsInRow+wi);
				}
			}
			
			//define uvt
			var uvt:Vector.<Number> = new Vector.<Number>();
			var uStep:Number = 1 / segmentCountW;
			var vStep:Number = 1 / segmentCountH;
			for (var vi:int = 0; vi < pointsInColumn; vi++ )
			{
				for (var ui:int = 0; ui < pointsInRow; ui++ )
				{
					uvt.push(ui*uStep, vi*vStep, NaN);
				}
			}
			
			
			return new ShapeData(gridPoints, indices, uvt);
			
		}
		
		
		

		static public function getCircle(radius:Number, height:Number):ShapeData
		{
			//calculate half of width, height and depth
			var w2:Number = radius;
			var h2:Number = height*0.5;
			var d2:Number = 15;
			
			var segmentCountH:int = 2;
			var segmentCountW:int = 20;
			var segmentH:Number = height / segmentCountH;
			var segmentW:Number = radius * 2 / segmentCountW;
			//define box points
			var points:Vector.<Vector3D> = new Vector.<Vector3D>();
			var depth:Number;
			var x:Number;
			for (var h:int = 0; h <= segmentCountH; h++ )
			{
				for (var w:int = 0; w <= segmentCountW; w++ )
				{
					x = w * segmentW -w2;
					depth = Math.sqrt(radius * radius - (Math.abs(x)) * (Math.abs(x)));
					//trace("Radius: " + radius + " radius-x: " + (radius - x));
					points.push(new Vector3D(x, h*segmentH-h2, -depth));
				}
			}
			
			/*points.push(new Vector3D(-w2, -h2, d2));
			points.push(new Vector3D(w2, -h2, d2));
			points.push(new Vector3D(w2, 0, 0));
			points.push(new Vector3D(-w2, 0, 0));
			points.push(new Vector3D(-w2, h2, d2));
			points.push(new Vector3D(w2, h2, d2));*/
			
 
			//define indices
			var indicesFront:Vector.<int> = new Vector.<int>();
			//indicesFront.push(0, 1, 3, 1, 2, 3, 3, 2, 4, 4, 2, 5);
			var pointsInRow:int = segmentCountW + 1;
			var pointsInColumn:int = segmentCountH + 1;
			for (var hi:int = 0; hi < segmentCountH; hi++ )
			{
				for (var wi:int = 0; wi < segmentCountW; wi++ )
				{
					indicesFront.push(hi*pointsInRow+wi, hi*pointsInRow+wi+1,(hi+1)*pointsInRow+wi);
					indicesFront.push(hi*pointsInRow+wi+1,(hi+1)*pointsInRow+wi+1,(hi+1)*pointsInRow+wi);
				}
			}
			
			//define uvt
			var uvtFront:Vector.<Number> = new Vector.<Number>();
			//uvtFront.push(0,0,NaN, 1,0,NaN, 1,0.5,NaN, 0, 0.5, NaN, 0,1,NaN, 1,1,NaN);
			var uStep:Number = 1 / segmentCountW;
			var vStep:Number = 1 / segmentCountH;
			for (var vi:int = 0; vi < pointsInColumn; vi++ )
			{
				for (var ui:int = 0; ui < pointsInRow; ui++ )
				{
					uvtFront.push(ui*uStep, vi*vStep, NaN);
				}
			}
			
			return new ShapeData(points, indicesFront, uvtFront);
		}
		
		static public function getPyramidData(width:Number, height:Number, depth:Number, texture:BitmapData):Array
		{
			//calculate half of width, height and depth
			var w2:Number = width*0.5;
			var h2:Number = height*0.5;
			var d2:Number = depth*0.5;
		 
			//define pyramid points (tip and 4 base corners)
			var points:Vector.<Vector3D> = new Vector.<Vector3D>();
			points.push(new Vector3D(0, -h2, 0));
			points.push(new Vector3D(w2, h2, -d2));
			points.push(new Vector3D(-w2, h2, -d2));
			points.push(new Vector3D(w2, h2, d2));
			points.push(new Vector3D(-w2, h2, d2));
		 
			//define indices
			var indicesFront:Vector.<int> = new Vector.<int>();
			indicesFront.push(0, 1, 2);
			var indicesBack:Vector.<int> = new Vector.<int>();
			indicesBack.push(0, 4, 3);
			var indicesLeft:Vector.<int> = new Vector.<int>();
			indicesLeft.push(0, 2, 4);
			var indicesRight:Vector.<int> = new Vector.<int>();
			indicesRight.push(0, 3, 1);
			var indicesBottom:Vector.<int> = new Vector.<int>();
			indicesBottom.push(2, 1, 4, 1, 3, 4);
		 
			//define uvt
			var uvtFront:Vector.<Number> = new Vector.<Number>();
			uvtFront.push(0.5, 0, NaN, 1, 1, NaN, 0, 1, NaN, NaN, NaN, NaN, NaN, NaN, NaN);
			var uvtBack:Vector.<Number> = new Vector.<Number>();
			uvtBack.push(0.5, 0, NaN, NaN, NaN, NaN, NaN, NaN, NaN, 1, 1, NaN, 0, 1, NaN);
			var uvtLeft:Vector.<Number> = new Vector.<Number>();
			uvtLeft.push(0.5, 0, NaN, NaN, NaN, NaN, 0, 1, NaN, NaN, NaN, NaN, 1, 1, NaN);
			var uvtRight:Vector.<Number> = new Vector.<Number>();
			uvtRight.push(0.5, 0, NaN, 0, 1, NaN, NaN, NaN, NaN, 1, 1, NaN, NaN, NaN, NaN);
			var uvtBottom:Vector.<Number> = new Vector.<Number>();
			uvtBottom.push(NaN, NaN, NaN, 1, 1, NaN, 0, 1, NaN, 1, 0, NaN, 0, 0, NaN);
		 
			return [points,
				[indicesFront, indicesBack, indicesLeft, indicesRight, indicesBottom],
				[uvtFront, uvtBack, uvtLeft, uvtRight, uvtBottom],
				[texture, texture, texture, texture, texture]];
		}
		
		
	}

}